/*
 * @Author: Dannycjh 2233879049@qq.com
 * @Date: 2023-04-15 11:57:11
 * @LastEditors: Dannycjh 2233879049@qq.com
 * @LastEditTime: 2023-04-28 14:47:26
 * @FilePath: \cc-game-development-template\assets\game.ts
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
/*
 * @Author: Dannycjh 2233879049@qq.com
 * @Date: 2023-04-15 11:57:11
 * @LastEditors: Dannycjh 2233879049@qq.com
 * @LastEditTime: 2023-04-17 09:04:56
 * @FilePath: \cc-game-development-template\assets\scripts\game.ts
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import { Animation, _decorator, Button, Color, Component, EventTouch, find, Label, macro, math, Node, NodeEventType, Prefab, Quat, resources, Sprite, SpriteFrame, Tween, tween, UITransform, v2, Vec2, Vec3, sys, view, instantiate, Collider, BoxCollider } from 'cc';
import { Constant, EventConst } from '../../common/Common';
import { App } from '../../App';
import { LoadMg } from '../../tools/LoadMg';
import UtilTool from '../../tools/UtilTool';
import AbstractComponent from '../../base/AbstractComponent';
import BilliardsGameController from './controller/BilliardsGameController';
import { GameModel } from './model/GameModel';
import wsTool from '../../net/ws';
import CommConst from '../../const/CommConst';
import { Ball } from './Ball';
import { RigidBodyComponent } from 'cc';
import { RigidBody } from 'cc';
import { Camera } from 'cc';
import { Cue } from './Cue';
import { PosBall } from './PosBall';

import { Role } from './Role';
import { PD } from '../../player/PlayerData';
import { desk } from '../desk';
import { ColliderComponent } from 'cc';
import { GameManage, GAME_MULTI_TYPE } from './GameManage';
import { UIOpacity } from 'cc';
import { loadCommonPreFab } from '../../tools/PopuUp';
import { ComboNode } from './ComboNode';
import { BallUI } from './BallUI';
const { ccclass, property } = _decorator;

let BALL_POS_INTERVAL = 1
let OFFEST_CNETER_X = 40

// 游戏主脚本
@ccclass('GameBaseModel')
export class GameBaseModel extends AbstractComponent {
    @property(Prefab)
    loadingBox: Prefab = null;

    @property(Prefab)
    gameLayerPrefab : Prefab = null;

    @property(Prefab)
    ballPrefab : Prefab  = null;

    @property(Prefab)
    ballUIPrefab : Prefab  = null; // 球呼吸的节点
    
    @property(Prefab)
    game3dPrefab : Prefab = null;

    @property(Node)
    viewNode : Node = null; // 界面父类

    @property(Node)
    effNode : Node = null; // 界面动效类
    
    // @property(Node)
    whiteBall : Node = null; // 白球

    // @property(Node)
    desk : Node = null;

    isPlayBall = false // 是否击打了白球

    gameLayer : Node = null // UI层界面

    camera2d : Camera = null
    camera3d : Camera = null

    roleNodeList : Node[] = [] //球洞列表

    edgeRoleNodeList : Node[] = [] //球洞碰撞体列表

    cueCtrl : Cue = null // 操作杆控制脚本

    posBallCtrl : PosBall = null // 摆球控制脚本

    // ballInRoleList : number[] = [] // 进球列表

    contactBallList : number[] = [] // 白球的碰球列表

    isOpenBallStatus = false // 是否是开球阶段

    ballHoleOpen = true // 球洞是否是开着的 true:开着

    public model:GameModel = null
    
    protected onLoad(): void {
        UtilTool.printLog("game init")

        LoadMg.removeLoad()
        LoadMg.loadPrefab = this.loadingBox;

        this.controller = new BilliardsGameController();
        this.controller.init(this);
        App.Model.gameController = this.controller as BilliardsGameController
        App.Model.gameView = this

        if (App.fightMode==App.fightMode_1 || App.fightMode==App.fightMode_2) {
            // 幸运场、练习场
            this.model = App.Model.gameModel
        }
        else if (App.fightMode==App.fightMode_3) {
            // 多人场
            this.model = App.Model.gameModel3
        }
        else{
            this.model = App.Model.gameModel
        }

        let layer = instantiate(this.gameLayerPrefab)
        layer.name = "gameLayer"
        layer.parent = this.viewNode /* find('Canvas') */
        this.gameLayer = layer

        let game3dNode = instantiate(this.game3dPrefab)
        
        this.node.addChild(game3dNode)

        this.whiteBall = find('whiteball', game3dNode)
        this.desk = find('desk', game3dNode)

        this.camera3d = find('Main Camera').getComponent(Camera)
        this.camera2d = find('Canvas/Camera').getComponent(Camera)
        
        // this.cueCtrl = find('gameNode/game_3d/whiteball/cueNode').getComponent(Cue)
        this.cueCtrl = find("Canvas/viewNode/cueNode2d").getComponent(Cue)

        this.posBallCtrl = find('gameNode/game_3d/pos_ball').getComponent(PosBall)

        // 获取球洞列表
        this.desk.children.forEach(item => {
            if (item.name.indexOf('wall_role') != -1) {
                this.roleNodeList.push(item.getChildByName('role'))
                this.edgeRoleNodeList.push(item.getChildByName('edge_role'))
            }
        })
        
        //用户信息更新
        App.Event.on(EventConst.UPDATA_USERINFO,(data)=>{
            PD.setCoin(data.ustd)
            PD.setStarCoin(data.experienceNum)
        })

        this.initEvent()
    }
    
    protected onEnable(): void {
        this._init();
    }
    
    start() {
        var that = this
        UtilTool.setScreen(this.node)
        this.playAudioEffect("game");
        // this.viewNode.setSiblingIndex(100)

        //监听击球事件“cue”
        App.Event.on(EventConst.GAME_CUE_END, this.onCue, this)
        
        // 初始化台球
        if (App.Model.gameManage.gameType == GAME_MULTI_TYPE.PT_FOURBALL || App.Model.gameManage.gameType == GAME_MULTI_TYPE.ZY_FOURBALL) {
            // 四球开伦
            this.initTableBallsFour()
        }
        else if (App.fightMode == App.fightMode_4) {
            // 血流到底
            tween(this.node).delay(0.1).call(()=>{
                this.initTableBallsBlood()
            }).start()
        }
        else{
            this.initTableBalls()
        }

        // tween(this.node).delay(1).call(() => {
        //     this.moveCue(0, 100)
        // }).start()

        // 游戏开始
        // wsTool.send({"msgCode":CommConst.Req_ENTERGAME})
        // .then(res =>{
        //     if (res?.result == 0) {
        //         this.model.init()
        //         this.model.setGameConfig(res)
                
        //     }
        //     else{
        //         alert('获取数据失败')
        //     }
        // })

    }

    private _init() {
        
    }

    initEvent() {
        App.Event.on(EventConst.BALL_IN_ROLE, this.ballInRoleEvent, this)
        App.Event.on(EventConst.BALL_IN_ROLE_SCORE, this.ballInRoleScoreEvent, this)
        App.Event.on(EventConst.ALLBALL_SLEEP, this.hitBallEndEvent, this)
        App.Event.on(EventConst.GAME_CUE_END, this.startHitBallEvent, this)
        App.Event.on(EventConst.WHITEBALL_CONTACT_BALL, this.whiteBallContactEvent, this)
        App.Event.on(EventConst.POS_WHITE_BALL_END, this.whiteBallPosEndEvent, this)
    }

    removeAllEvent() {
        App.Event.off(EventConst.BALL_IN_ROLE, this.ballInRoleEvent, this)
        App.Event.off(EventConst.BALL_IN_ROLE_SCORE, this.ballInRoleScoreEvent, this)
        App.Event.off(EventConst.ALLBALL_SLEEP, this.hitBallEndEvent, this)
        App.Event.off(EventConst.GAME_CUE_END, this.startHitBallEvent, this)
        App.Event.off(EventConst.WHITEBALL_CONTACT_BALL, this.whiteBallContactEvent, this)
        App.Event.off(EventConst.POS_WHITE_BALL_END, this.whiteBallPosEndEvent, this)
    }
    
    /**
     * 游戏开始
     */
    gameStart() {

    }

    // 收到球进洞事件
    ballInRoleEvent(ballNumber : number, ballNode : Node, role : Role) {
        // this.ballInRoleList.push(ballNumber)
    }

    // 展示得分区域分数动画
    showChangeScore_Area() {
    }
    // 收到球进洞得分事件
    ballInRoleScoreEvent(ballNumber: number, oneRoleNumber: number, role : Role) {
      
    }

    // 收到开始击球事件
    startHitBallEvent(event) {
        UtilTool.printLog("start hit ball")
    }
    
    // 收到白球碰撞球事件
    whiteBallContactEvent(ballNumber : number) {
        if (this.contactBallList.indexOf(ballNumber)==-1) {
            this.contactBallList.push(ballNumber)
        }
    }

    // 收到摆球结束事件
    whiteBallPosEndEvent(whiteBallPos) {
        UtilTool.printLog("white ball pos end", whiteBallPos)
    }

    // 收到击球结束事件
    hitBallEndEvent() {
        UtilTool.printLog("hit ball end")
    }

    // 获取GameLayer节点的脚本
    getGameLayerCtrl(layerCtrl : typeof Component) : any {
        let layer = find('gameLayer',this.viewNode)
        return layer.getComponent(layerCtrl)
    }

    // 初始化四球开伦台球，台球一字型排列
    initTableBallsFour() {
    }

    // 初始化血流到底台球
    initTableBallsBlood() {
    }

    // 初始化台球，台球箭状排列
    initTableBalls() {
        App.Model.gameManage.otherBallArr = []
        App.Model.gameManage.otherBallArrUI = []

        let index = 0

        for(let i=1; i<6; i++) {

            for(let j=0; j<i; j++) {
                let ball = instantiate(this.ballPrefab);
                this.node.addChild(ball);

                index++
                ball.getComponent(Ball).init(index);
                App.Model.gameManage.otherBallArr.push(ball)

                if (this.ballUIPrefab) { // 在场景里面添加这个预制件才有球呼吸功能
                    let ballAbelNode = instantiate(this.ballUIPrefab);
                    ballAbelNode.getComponent(BallUI).init(index);
                    this.node.addChild(ballAbelNode);
                    App.Model.gameManage.otherBallArrUI.push(ballAbelNode)
                }
                
            }
        }

        this.resetAllBallPos()

    }

    // 通过球号获取球
    getBallByNumber(number : number, whiteBallNumber:number = 0) : Ball {
        if(number == whiteBallNumber) {
            return this.whiteBall.getComponent(Ball)
        }

        let ballList = App.Model.gameManage.otherBallArr.filter(ball => {
            let ballNumber = ball.getComponent(Ball).number
            return ballNumber == number
        })
        return ballList[0].getComponent(Ball)
    }

    /**  
     * 重新摆球
     * @param isMine 是否是自己击打
    */
    resetAllBallPos(isMine=true) {
        this.isOpenBallStatus = true
        this.setBallSleep(this.whiteBall)
        if (App.fightMode == App.fightMode_1) { 
            this.whiteBall.position = new Vec3(-78, 1, this.getRandom(-65,65));
        }
        else {
            this.whiteBall.position = new Vec3(-78, 1, 0);
        }
        
        this.whiteBall.getComponent(Ball).init(0);
        this.whiteBall.getComponent(Ball).setDefault();
        this.whiteBall.active = true

        let index = 0
        for(let i=1; i<6; i++) {

            for(let j=0; j<i; j++) {
                index++
                let ball = this.getBallByNumber(index)
                this.setBallSleep(ball)

                let width = ball.getWidth();

                let totalWidth = (i - 1)* BALL_POS_INTERVAL + i * width
                
                let x = (i-1)*(BALL_POS_INTERVAL+width*Math.sqrt(3)/2) + OFFEST_CNETER_X;

                let z = -totalWidth / 2 + j * (BALL_POS_INTERVAL+width) + width / 2;
                let pos = new Vec3(x, 1, z);
     
                ball.node.setPosition(pos);
                ball.node.active = true
                ball.setDefault()
            }
        }
        
        this.cueCtrl.showCue(isMine)
        this.randomAllBallPos()
    }

    /**  
     * 摆球位置随机摆放
    */
    randomAllBallPos() {
        if (App.fightMode == App.fightMode_1) { // 幸运台球模式
            let posNumbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
            for (let i = 0; i < App.Model.gameManage.otherBallArr.length; i++) {
                let item = App.Model.gameManage.otherBallArr[i]

                // 获取随机数
                let indexPos = this.getRandom(0,posNumbers.length-1)
                let indexValue = posNumbers[indexPos]
                posNumbers.splice(indexPos, 1)

                item.getComponent(Ball).init(indexValue)

                let itemUI = App.Model.gameManage.otherBallArrUI[i]
                if (itemUI) {
                    itemUI.getComponent(BallUI).init(indexValue)
                }
            }
        }
    }

    // 一杆清台
    showAllOneClear() {
        let comboNode = instantiate(App.Scene.getRes("prefabs/game/comboNode"))
        comboNode.parent = find("Canvas/viewNode")
        
        comboNode.getComponent(ComboNode).showAllOneClear()
    }

    // 显示连杠的图片
    showCombo(combo : number) {

        let comboNode = instantiate(App.Scene.getRes("prefabs/game/comboNode"))
        comboNode.parent = find("Canvas/viewNode")
        
        comboNode.getComponent(ComboNode).showCombo(combo)

    }

    // 决战时刻(四球开伦)
    showDecisiveMoment() {
        let comboNode = instantiate(App.Scene.getRes("prefabs/game/comboNode"))
        comboNode.parent = find("Canvas/viewNode")
        
        comboNode.getComponent(ComboNode).showAllOneClear()
    }

    /**  
     * 获取范围随机数
     * @param startNumber 随机数开始下标
     * @param endNumber 随机数结束下标
    */
    getRandom(startNumber, endNumber) {
        var choice = endNumber - startNumber + 1
        return Math.floor(Math.random() * choice + startNumber)
    }

    /**  
     * 获取白球到1号球位置的角度
    */
    getWhiteToOneAngle() {
        let oneBall = App.Model.gameManage.otherBallArr[0]
        let angle = this.getAngleByWorld(this.whiteBall.worldPosition, oneBall.worldPosition)

        return angle
    }

    /**  
     * 操作杆结束后回调
    */
    onCue (event) {
        // 给白球施加冲量,触发运动
        let targetPos = UtilTool.getPointMove(event.angle, Math.abs(event.force))
        let param2 = App.Model.gameManage.getActionPointImpulse(this.cueCtrl.cueAngle)
        this.whiteBall.getComponent(RigidBody).applyLocalImpulse(new Vec3(targetPos.x, -App.Model.gameManage.getBallActionDial(), targetPos.y), param2);
        UtilTool.printLog("run wBall", param2.x, param2.y, param2.z)

        //击打了白球
        this.isPlayBall = true;
    }

    // 操作杆移动
    moveCue(cueAngle, force) {
        this.cueCtrl.cueMoveAction(cueAngle, force)
    }

    update(deltaTime: number) {
        let that = this
        // 执行停止球后回调
        let ballStopFunc = function(sleep){ 
            if (sleep && that.isPlayBall) {
                that.showChangeScore_Area()

                //白球从运动到停止，状态切换时，标记设置为false，并发送白球停止的事件
                that.isPlayBall = false;
                            
                App.Event.emit(EventConst.ALLBALL_SLEEP)
            }
        }
        
        // 判断所有的球是不是都是休眠状态
        if (this.whiteBall.getComponent(RigidBody).isSleeping || !this.whiteBall.active) {
            let allIsSleeping = true
            for (let i = 0; i < App.Model.gameManage.otherBallArr.length; i++) {
                const ball = App.Model.gameManage.otherBallArr[i]
                if (ball && ball.active) {
                    let isSleeping = ball.getComponent(Ball).getComponent(RigidBody).isSleeping
                    if (!isSleeping) {
                        allIsSleeping = false
                        break
                    }
                }
            }

            ballStopFunc(allIsSleeping)
        }
        
    }

    /**  
     * 设置球休眠
    */
    setBallSleep(ball) {
        if (ball) {
            ball.getComponent(RigidBody).setLinearVelocity(new Vec3(0, 0, 0));
            ball.getComponent(RigidBody).setAngularVelocity(new Vec3(0, 0, 0));
            ball.getComponent(RigidBody).sleep()
        }
    }

    /**  
     * 设置球洞开关
     * @param open [true]开 [false]关
    */
    setHoleSwitch(open) {
        this.ballHoleOpen = open
        
        this.edgeRoleNodeList.forEach(item => {
            item.getComponent(BoxCollider).isTrigger = open
        })

        this.roleNodeList.forEach(item => {
            item.active = open
        })

        // 球洞关闭UI显示
        if (find("holeNode",this.gameLayer)) {
            find("holeNode",this.gameLayer).active = !open
        }
    }

    /**  
     * 设置目标球定位图标
     * @param open 是否显示定位
     * @param num 球号
    */
    setBallOrientation(open,num=0) {
        if (open) {
            for (let i = 0; i < App.Model.gameManage.otherBallArr.length; i++) {
                const ball = App.Model.gameManage.otherBallArr[i]
                if (ball) {
                    let ballObj = ball.getComponent(Ball)
                    let ballNumber = ballObj.number
                    ballObj.isOrientation = false
                    if (ballNumber == num) {
                        let worldPos = ballObj.node.worldPosition
                        let localPos = this.World3DToLocal(worldPos)
    
                        // 显示定位
                        let posNode = find("posNode",this.gameLayer)
                        if (posNode) {
                            posNode.active = true
                            posNode.setPosition(localPos.x, localPos.y+70)
                            ballObj.isOrientation = true

                            Tween.stopAllByTarget(posNode)
                            let embedTween = tween(posNode)
                                .by(0.3, { position: new Vec3(0, 15, 0) })
                                .by(0.4, { position: new Vec3(0, -15, 0) })
                                .call(() => {
                                })
                            tween(posNode)
                                .repeatForever(embedTween)
                                .start()
                            }
                    }
                }
            }
        }
        else {
            let posNode = find("posNode",this.gameLayer)
            if (posNode) {
                Tween.stopAllByTarget(posNode)
                posNode.active = false
            }

            for (let i = 0; i < App.Model.gameManage.otherBallArr.length; i++) {
                const ball = App.Model.gameManage.otherBallArr[i]
                if (ball) {
                    let ballObj = ball.getComponent(Ball)
                    ballObj.isOrientation = false
                }
            }
        }
        
    }

    /**   
     * 屏幕转3D世界坐标
    */
    screenToWorld(screenPos:Vec2) {
        let camera3D = this.camera3d
        let c_z = camera3D.node.worldPosition.y / camera3D.getComponent(Camera).far
        let pos = new Vec3()
        camera3D.screenToWorld(new Vec3(screenPos.x, screenPos.y, c_z), pos)
        return pos
    }

    /**   
     * 3D世界坐标转UI坐标
    */
    World3DToUI(worldPos:Vec3) {

        let camera3D = this.camera3d
        let cameraUI = this.camera2d

        let sceenPoint = camera3D.worldToScreen(worldPos);
        let uiPos = cameraUI.screenToWorld(sceenPoint);

        let vs = view.getVisibleSize()
        return new Vec2(uiPos.x-vs.width/2, uiPos.y-vs.height/2)

        // return new Vec2(uiPos.x, uiPos.y)
    }

    /**   
     * 3D世界坐标转本地坐标
    */
    World3DToLocal(worldPos:Vec3) {
        let camera3D = this.camera3d
        let cameraUI = this.camera2d

        let sceenPoint = camera3D.worldToScreen(worldPos);
        let uiPos = cameraUI.screenToWorld(sceenPoint);

        let vs = view.getVisibleSize()
        return new Vec2(uiPos.x-vs.width/2, uiPos.y-vs.height/2)
    }

    /**   
     * 本地坐标转3D世界坐标
    */
    localToWorld3D(localPos:Vec2) {

        let cameraUI = this.camera2d

        let vs = view.getVisibleSize()
        let uiPos = new Vec2(localPos.x+vs.width/2, localPos.y+vs.height/2)
        let sceenPoint = cameraUI.worldToScreen(new Vec3(uiPos.x, uiPos.y ,0))
        return this.screenToWorld(new Vec2(sceenPoint.x ,sceenPoint.y))
    }

    /**   
     * 通过世界坐标获取两点间的角度
    */
    getAngleByWorld(worldPos1:Vec3, worldPos2:Vec3) {
        let angle = UtilTool.getPointAngle(new Vec2(worldPos1.x, worldPos1.z), new Vec2(worldPos2.x, worldPos2.z))
        angle = UtilTool.getRAngle(angle)

        return angle
    }

    /**   
     * 通过UI坐标获取两点间的角度
    */
    getAngleByUI(worldPos1:Vec2, worldPos2:Vec2) {
        let angle = UtilTool.getPointAngle(new Vec2(worldPos1.x, worldPos1.y), new Vec2(worldPos2.x, worldPos2.y))
        angle = UtilTool.getRAngle(angle)

        return angle
    }
    
    /**
     * 游戏结束
     */
    public gameOver(data) {
    }
    
    /**
     * 播放音乐
     */
    public playAudioEffect(name: string) {
        App.Audio.play(name)
    }

    onDestroy() {
        App.Event.off(EventConst.UPDATA_USERINFO)
        this.removeAllEvent()
    }

    /////////////////////////////////////////////////////////////// 请求消息 ////////////////////////////////////////////////////////////////////

    /**
     * 请求认输
     */
    reqGiveUpMsg(cb) {
    }

    /**
     * 请求客户端开球
     */
    reqOpenBallMsg(allBallInfo) {
    }

    /**
     * 请求调整球杆角度
     */
    reqChangeCueMsg(cueAngle) {
    }

    /**
     * 请求玩家击球
     * @param angle 球的角度
     * @param dynamics 力度
     */
    reqHitballMsg(angle, dynamics) {
    }

    /**
     * 请求球桌运动结束，上报击球结果
     * @param motherBall 0:母球正常打球 1:母球没有击中任何球 2:母球先击中别家球 3:母球进洞了
     * @param enterBall 进洞的球号
     * @param allBallInfo 各个球的信息
     */
    reqHitballEndMsg(motherBall, enterBall, allBallInfo, wheel) {
    }

    /**
     * 请求上报重新摆球的结果
     */
    reqPosBallResoultMsg(allBallInfo) {
    }

    /**
     * 请求退出观战
     */
    reqQuitWatchMsg() {
    }

    /////////////////////////////////////////////////////////////// 请求消息 ////////////////////////////////////////////////////////////////////
}

